package com.openlogic.flow.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dozer.inject.Inject;
import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.entity.Instance;
import org.dromara.warm.flow.core.entity.Task;
import org.dromara.warm.flow.core.entity.User;
import org.dromara.warm.flow.core.enums.UserType;
import org.dromara.warm.flow.core.service.InsService;
import org.dromara.warm.flow.core.service.TaskService;
import org.dromara.warm.flow.core.utils.CollUtil;
import org.dromara.warm.flow.core.utils.ObjectUtil;
import org.dromara.warm.flow.core.utils.StreamUtils;
import org.dromara.warm.flow.orm.entity.FlowDefinition;
import org.dromara.warm.flow.orm.entity.FlowHisTask;
import org.dromara.warm.flow.orm.entity.FlowInstance;
import org.dromara.warm.flow.orm.entity.FlowTask;
import org.dromara.warm.flow.orm.entity.FlowUser;
import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper;
import org.dromara.warm.flow.orm.mapper.FlowTaskMapper;
import org.noear.solon.annotation.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.solon.service.impl.ServiceImpl;
import com.openlogic.common.core.exception.ServiceException;
import com.openlogic.common.core.web.domain.entity.SysDept;
import com.openlogic.common.core.web.domain.entity.SysRole;
import com.openlogic.common.core.web.domain.entity.SysUser;
import com.openlogic.common.security.utils.SecurityUtil;
import com.openlogic.flow.common.constants.FlowConstant;
import com.openlogic.flow.domain.dto.StartFlowTaskDto;
import com.openlogic.flow.domain.entity.FlowCategory;
import com.openlogic.flow.domain.query.FlowTaskQuery;
import com.openlogic.flow.domain.vo.FlowHisTaskVo;
import com.openlogic.flow.domain.vo.FlowTaskVo;
import com.openlogic.flow.domain.vo.StartFlowTaskVo;
import com.openlogic.flow.mapper.FlowCategoryMapper;
import com.openlogic.flow.service.IFlowTaskService;
import com.openlogic.flow.utils.OpenLogicFLowUtils;
import com.openlogic.system.api.RemoteSysUserService;

import cn.hutool.core.convert.Convert;

/**
 * @ClassName: FlowTaskServiceImpl
 * @Description: 流程任务 服务接口实现层
 * @author weiyongpeng
 * @date 2025年2月13日 下午8:11:48
 */
@Component
public class FlowTaskServiceImpl extends ServiceImpl<FlowTaskMapper, FlowTask> implements IFlowTaskService {

	private static final Logger log = LoggerFactory.getLogger(FlowTaskServiceImpl.class);
	
	@Inject
	TaskService taskService;

	@Inject
	InsService insService;
	
	@Inject
	RemoteSysUserService remoteSysUserService;

	@Inject
	FlowTaskMapper flowTaskMapper;
    
	@Inject
	FlowInstanceMapper flowInstanceMapper;
	
	@Inject
	FlowCategoryMapper flowCategoryMapper;

	@Override
	public StartFlowTaskVo startWorkFlow(StartFlowTaskDto startFlowTaskDto) {
		String businessId = startFlowTaskDto.getBusinessId();
		if (StringUtils.isBlank(businessId)) {
			throw new ServiceException("启动工作流时必须包含业务ID");
		}
		// 启动流程实例（提交申请）
		Map<String, Object> variables = startFlowTaskDto.getVariables();
		// 流程发起人
		variables.put(FlowConstant.INITIATOR, SecurityUtil.getUserId());
		// 业务id
		variables.put(FlowConstant.BUSINESS_ID, businessId);
		FlowInstance flowInstance = flowInstanceMapper
				.selectOneByQuery(QueryWrapper.create().eq(FlowInstance::getBusinessId, businessId));
		if (ObjectUtil.isNotNull(flowInstance)) {
//            BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus());
			List<Task> taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId()));
			StartFlowTaskVo dto = new StartFlowTaskVo();
			dto.setProcessInstanceId(taskList.get(0).getInstanceId());
			dto.setTaskId(taskList.get(0).getId());
			return dto;
		}
		FlowParams flowParams = new FlowParams();
		flowParams.flowCode(startFlowTaskDto.getFlowDefCode());
		flowParams.variable(startFlowTaskDto.getVariables());
//		flowParams.flowStatus(BusinessStatusEnum.DRAFT.getStatus());
		Instance instance;
		try {
			instance = insService.start(businessId, flowParams);
		} catch (Exception e) {
			throw new ServiceException(e.getMessage());
		}
		// 申请人执行流程
		List<Task> taskList = taskService.list(new FlowTask().setInstanceId(instance.getId()));
		if (taskList.size() > 1) {
			throw new ServiceException("请检查流程第一个环节是否为申请人！");
		}
		StartFlowTaskVo dto = new StartFlowTaskVo();
		dto.setProcessInstanceId(instance.getId());
		dto.setTaskId(taskList.get(0).getId());
		return dto;
	}

	@Override
	public List<FlowTaskVo> listUndoByPage(FlowTaskQuery flowTask) {
		SysUser loginUser = SecurityUtil.getLoginUser().getSysUser();
        List<String> permissionList = permissionList(String.valueOf(loginUser.getUserId()), loginUser.getDeptId(), loginUser);
        flowTask.setPermissionList(permissionList);
        List<FlowTaskVo> list = this.unDoList(flowTask);
        List<Long> taskIds = StreamUtils.toList(list, FlowTaskVo::getId);
        List<User> userList = OpenLogicFLowUtils.getFlowUserService().getByAssociateds(taskIds);
        Map<Long, List<User>> map = StreamUtils.groupByKey(userList, User::getAssociated);
        for (FlowTaskVo taskVo : list) {
            if (ObjectUtil.isNotNull(taskVo)) {
                List<User> users = map.get(taskVo.getId());
                if (CollectionUtils.isNotEmpty(users)) {
                    for (User user : users) {
                        if (UserType.APPROVAL.getKey().equals(user.getType())) {
                            if (StringUtils.isEmpty(taskVo.getApprover())) {
                                taskVo.setApprover("");
                            }
                            String name = getName(user.getProcessedBy());
                            if (StringUtils.isNotEmpty(name)) taskVo.setApprover(taskVo.getApprover().concat(name).concat(";"));
                        } else if (UserType.TRANSFER.getKey().equals(user.getType())) {
                            if (StringUtils.isEmpty(taskVo.getTransferredBy())) {
                                taskVo.setTransferredBy("");
                            }
                            String name = getName(user.getProcessedBy());
                            if (StringUtils.isNotEmpty(name)) taskVo.setTransferredBy(taskVo.getTransferredBy().concat(name).concat(";"));
                        } else if (UserType.DEPUTE.getKey().equals(user.getType())) {
                            if (StringUtils.isEmpty(taskVo.getDelegate())) {
                                taskVo.setDelegate("");
                            }
                            String name = getName(user.getProcessedBy());
                            if (StringUtils.isNotEmpty(name)) taskVo.setDelegate(taskVo.getDelegate().concat(name).concat(";"));
                        }
                    }
                }
            }
        }
		return list;
	}
	
	@Override
	public List<FlowHisTaskVo> listhasDoneByPage(FlowTaskQuery flowTask) {
		QueryWrapper wrapper = buildQueryWrapper(flowTask);
		List<FlowHisTaskVo> list = this.doneList(wrapper);
		return list;
	}
	
	private List<FlowHisTaskVo> doneList(QueryWrapper wrapper) {
		wrapper.select("a.id,\r\n"
				+ "                a.node_code,\r\n"
				+ "                a.node_name,\r\n"
				+ "                a.cooperate_type,\r\n"
				+ "                a.approver,\r\n"
				+ "                a.collaborator,\r\n"
				+ "                a.node_type,\r\n"
				+ "                a.target_node_code,\r\n"
				+ "                a.target_node_name,\r\n"
				+ "                a.definition_id,\r\n"
				+ "                a.instance_id,\r\n"
				+ "                a.flow_status flow_task_status,\r\n"
				+ "                a.message,\r\n"
				+ "                a.ext,\r\n"
				+ "                a.create_time,\r\n"
				+ "                a.update_time,\r\n"
				+ "                a.tenant_id,\r\n"
				+ "                a.form_custom,\r\n"
				+ "                a.form_path,\r\n"
				+ "                b.flow_status,\r\n"
				+ "                b.business_id,\r\n"
				+ "                b.create_by,\r\n"
				+ "                c.flow_name,\r\n"
				+ "                c.flow_code,\r\n"
				+ "                c.category,\r\n"
				+ "                c.version")
		.from(FlowHisTask.class).as("a")
		.leftJoin(FlowInstance.class).as("b").on("a.instance_id = b.id")
		.leftJoin(FlowDefinition.class).as("c").on("a.definition_id = c.id")
		.where("a.del_flag = '0'").and("b.del_flag = '0'").and("c.del_flag = '0'")
		.and("a.node_type in ('1','3','4')");
		List<FlowHisTaskVo> list = listAs(wrapper, FlowHisTaskVo.class);

		return list;
	}

	private List<FlowTaskVo> unDoList(FlowTaskQuery flowTask) {
		QueryWrapper queryWrapper = QueryWrapper.create()
		        .select("distinct t.id,\r\n"
		        		+ "            t.node_code,\r\n"
		        		+ "            t.node_name,\r\n"
		        		+ "            t.node_type,\r\n"
		        		+ "            t.definition_id,\r\n"
		        		+ "            t.instance_id,\r\n"
		        		+ "            t.create_time,\r\n"
		        		+ "            t.update_time,\r\n"
		        		+ "            t.tenant_id,\r\n"
		        		+ "            i.business_id,\r\n"
		        		+ "            i.flow_status,\r\n"
		        		+ "            i.activity_status,\r\n"
		        		+ "            d.flow_name,\r\n"
		        		+ "            t.form_custom,\r\n"
		        		+ "            t.form_path")
		        .from(FlowTask.class).as("t")
		        .leftJoin(FlowUser.class).as("uu").on("uu.associated = t.id")
		        .leftJoin(FlowDefinition.class).as("d").on("t.definition_id = d.id")
		        .leftJoin(FlowInstance.class).as("i").on("t.instance_id = i.id")
		        .where("t.node_type = 1");
		if (ObjectUtil.isNotNull(flowTask.getPermissionList()) && flowTask.getPermissionList().size() > 0) {
			queryWrapper.in("uu.processed_by",flowTask.getPermissionList());
		}
		if (ObjectUtil.isNotNull(flowTask.getNodeCode()) && !flowTask.getNodeCode().equals("")) {
			queryWrapper.and("t.node_code = #{nodeCode}",flowTask.getNodeCode());
		}
		if (ObjectUtil.isNotNull(flowTask.getPermissionList()) && flowTask.getPermissionList().size() > 0) {
			queryWrapper.in("uu.processed_by",flowTask.getPermissionList());
		}
		if (ObjectUtil.isNotNull(flowTask.getPermissionList()) && flowTask.getPermissionList().size() > 0) {
			queryWrapper.in("uu.processed_by",flowTask.getPermissionList());
		}
		List<FlowTaskVo> list = listAs(queryWrapper, FlowTaskVo.class);
		return list;
	}

	/**
	 * 描述：获取权限
	 * @Title: permissionList
	 * @param userId 用户编号
	 * @param deptId 部门编号
	 * @param sysUser 登陆用户
	 * @return 权限列表
	 * @author weiyongpeng
	 * @date 2025年2月18日 下午8:47:21
	 */
    private List<String> permissionList(String userId, Long deptId, SysUser sysUser) {
        List<SysRole> roles = sysUser.getRoles();
        List<String> permissionList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(roles)) {
            permissionList = StreamUtils.toList(roles, role -> "role:" + role.getRoleId());
        }
        permissionList.add(userId);
        if (Objects.nonNull(deptId)) {
            permissionList.add("dept:" + deptId);
        }
        log.info("当前用户所有权限[{}]", permissionList);
        return permissionList;
    }
    
    private String getName(String id) {
        Map<Long, String> userMap = StreamUtils.toMap(remoteSysUserService.selectUserList(new SysUser()).getData()
                , SysUser::getUserId, SysUser::getNickName);
        Map<Long, String> deptMap = StreamUtils.toMap(remoteSysUserService.selectDeptList(new SysDept()).getData()
                , SysDept::getDeptId, SysDept::getDeptName);
        Map<Long, String> roleMap = StreamUtils.toMap(remoteSysUserService.selectRoleList(new SysRole()).getData()
                , SysRole::getRoleId, SysRole::getRoleName);
        if (StringUtils.isNotBlank(id)) {
            if (id.contains("user:")) {
                String name = userMap.get(Long.valueOf(id.replace("user:", "")));
                if (StringUtils.isNotEmpty(name)) {
                    return "用户:" + name;
                }
            } else if (id.contains("dept:")) {
                String name = deptMap.get(Long.valueOf(id.replace("dept:", "")));
                if (StringUtils.isNotEmpty(name)) {
                    return "部门:" + name;
                }
            } else if (id.contains("role")) {
                String name = roleMap.get(Long.valueOf(id.replace("role:", "")));
                if (StringUtils.isNotEmpty(name)) {
                    return "角色:" + name;
                }
            } else {
                try {
                    long parseLong = Long.parseLong(id);
                    String name = userMap.get(parseLong);
                    if (StringUtils.isNotEmpty(name)) {
                        return "用户:" + name;
                    }
                } catch (NumberFormatException e) {
                    return id;
                }
            }
        }
        return "";
    }
    
    private QueryWrapper buildQueryWrapper(FlowTaskQuery flowTaskQuery) {
		QueryWrapper wrapper = QueryWrapper.create();
		if (StringUtils.isNotBlank(flowTaskQuery.getNodeName())) {
			wrapper.like(FlowHisTask::getNodeName, flowTaskQuery.getNodeName());
		}
		if (StringUtils.isNotBlank(flowTaskQuery.getFlowName())) {
			wrapper.like(FlowHisTask::getFlowName, flowTaskQuery.getFlowName());
		}
		if (CollUtil.isNotEmpty(flowTaskQuery.getCreateByIds())) {
			wrapper.in(FlowHisTask::getCreateBy, flowTaskQuery.getCreateByIds());
		}
        if (StringUtils.isNotBlank(flowTaskQuery.getCategory())) {
        	List<Integer> categoryIds = Stream.concat(flowCategoryMapper.selectListByPid(flowTaskQuery.getCategory()).stream().map(FlowCategory::getId), Stream.of(Integer.valueOf(flowTaskQuery.getCategory()))).collect(Collectors.toList());
            wrapper.in("t.category", StreamUtils.toList(categoryIds, Convert::toStr));
        }
        wrapper.orderBy(FlowHisTask::getCreateTime).desc();
        return wrapper;
    }

}
